"""
#===============================================================================
# License
#===============================================================================
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

#===============================================================================
Copyright (c) 2009 bryan p. conrad
bconrad2@gmail.com

Repository Path:    
$$HeadURL$$

Created on:           7/27/2009
Last Revised on:      $$Date$$
Version:              $$Revision$$
ID:                   $$Id$$
Author:               $$Author$$ :: (bryan)

#===============================================================================


#===============================================================================
# Documentation
#===============================================================================
This class provides a tool to store, archive, read and access image 
registration data.  Data is archived in an xml format that can be then 
be loaded into the data structure to restore a session.  This class also
provides a number of methods for setting and getting different types of 
data elements in the structure.  The general concept is that data is stored 
in a tree of nodes.  The top level node is: Tree, its children are: Frame,
RegistrationSettings, OptimizerSettings, MetricSettings, PatientInfo.  Frame
has two children: FixedImage and Volume.  FixedImage has two children: Cal 
and path.  Volume has five children: current_pose, starting_pose, 
optimized_pose, ROI, path.

In general, poses are referenced by a pair of (FixedImage, Volume) filenames.
All nodes will be searched for the given FixedImage and then that Frame will
be searched for an occurrence of Volume.  Convience methods are provided
to set/get the current pose based on the indices: currentVolume, currentImage.

Some examples of how this data structure can be used:
# Pull the Volume and FixedImage elements out of the data structure
Volume = data.Tree.Frame[vert].Volume[0]
FixedImage = data.Tree.Frame[vert].FixedImage[proj]       

# Load all of the data elements from the structure into the reg class
self.reg.LoadCalFile(data.get_path(FixedImage.Cal))
#self.reg.LoadStaFile(data.get_path(Volume.STA))
self.reg.SetVolumeFileName(data.get_path(Volume))
self.reg.SetFixedImageFileName(data.get_path(FixedImage))
self.reg.SetRegionOfInterest(data.get_roi(Volume))
self.reg.SetCenter(data.get_center(Volume, body))
"""

#===============================================================================
# Imports
#===============================================================================
import JT
import pylab
import sys
from lxml import etree, objectify
from PyQt4 import QtGui,QtCore
import re
import numpy as np
import os
#===============================================================================
# Generic Data Structure
#===============================================================================


class DataStructure(object):
    """
    A container for pyTrack data. 
    """
    def __init__(self):
        # Build initial empty data structure
        self.Tree = objectify.Element('Tree')
        # These two parameter are key to determining current node location
        # they must be updated every time a volume or image is changed/selected
        self.Tree.current_image = 0
        self.Tree.current_volume = 0
        
        PatientInfo = etree.SubElement(self.Tree, 'PatientInfo')
        OptimizerSettings = etree.SubElement(self.Tree, 'OptimizerSettings')
        MetricSettings = etree.SubElement(self.Tree, 'MetricSettings')
        RegistrationSetings = etree.SubElement(self.Tree, 'RegistrationSettings')
        DisplaySetings = etree.SubElement(self.Tree, 'DisplaySettings')
        self.set_global_display_settings(60, 0.15)
        self._clean_descendents = self.countdescendants()
        self.BackupData = None
        
    def find_node(self,image_path,volume_path):
        """
        This method will return the node for the given image/volume pair
        
        Not implemented yet, but in looking at how often the following code
        pattern is called (at least 10 times), it looks like a prime 
        candidate to be refactored:
        
        image_iter = self.Tree.iter(tag = 'FixedImage')
        for image in image_iter:
            if image.path == image_path:
                Frame = image.getparent()
                vol_iter = Frame.iter(tag = 'Volume')
                for vol in vol_iter:
                    if vol.path == volume_path:
        
        """
    
    def add_frame(self):
        """
        Add a new frame to the data structure, each frame contains one
        or more volumes and one or more fixed images.  Returns the new
        Frame element so that fixed images and volumes can be added to it.
        """
        Frame = etree.SubElement(self.Tree,'Frame')
        return Frame
    
    
    def add_fixed_image(self,path,Frame=None):
        """
        To add a fixed image to the data structure, supply the path
        to the image location.
        
        cal_path is an optional parameter which specifies the path to the image 
        calibration file
        
        Returns the newly created FixedImage element, so that a calibration 
        can be added to it.
        """
        if Frame is None:
            Frame = self.add_frame()
            FixedImage = etree.SubElement(Frame, 'FixedImage')
            FixedImage.path = path
        else:
            try:
                Frame.FixedImage.path = path
            except:
                FixedImage = etree.SubElement(Frame, 'FixedImage')
                Frame.FixedImage.path = path
        return Frame
    
    def add_image_sequence(self,image_path_list,single_plane=True):
        """
        Add a sequence of multiple images to the data structure.
        image_path_list is a python list of filepaths to images.  
        
        If single_plane is True, then the old image paths will be removed,
        so that only one image path exists for each frame.  If single_plane
        is False, then the new image paths are appended to the frame, 
        allowing multiple fixed images to be added to each frame.
        """
        n_images = len(image_path_list)
        n_frames = self.frame_count()
        i = 0
        # if the number of frames is not 0 or equal to the number of images
        # given in the list, there is a problem
        if (n_frames == n_images):
            iter = self.Tree.iter(tag = 'Frame')
            for i, frame in enumerate(iter):
                if single_plane:
                    # if there is an existing path, get rid of it.
                    try:
                        old_path = frame.FixedImage.path
                        frame.FixedImage.remove(old_path)
                    except:
                        # no old path, is ok i guess
                        # What if there is no fixed image node?
                        pass
                    # assign the image path to the existing FixedImage node
                    frame.FixedImage.path = image_path_list[i]
                else:
                    # multiple fixed images, need to add a new FixedImage node
                    self.add_fixed_image(image_path_list[i])
            
        elif (n_frames == 0):
            for i, img in enumerate(image_path_list):
                self.add_fixed_image(img)
        else:
            print "The number of images supplied (%i) does not correspond "\
                  "to the number of existing frames (%i)" % (n_images, n_frames)
            return
        print 'Added %i images' % (i+1)
    
    def get_current_image_path(self):
        """
        returns the path to the current image
        """
        image_list = []
        # First find the specified pose node
        iter = self.Tree.iter(tag = 'FixedImage')
        for item in iter:
            image_list.append(item.path)
        return str(image_list[int(self.Tree.current_image)])
    
    def get_current_volume_path(self):
        """
        returns the path to the current volume (of the current image)
        """
        volume_list = []
        current_image = self.get_current_image_path()
        # First find the specified pose node
        image_iter = self.Tree.iter(tag = 'FixedImage')
        for image in image_iter:
            if image.path == current_image:
                Frame = image.getparent()
                vol_iter = Frame.iter(tag = 'Volume')
                for vol in vol_iter:
                    volume_list.append(vol.path)
        try:
            return str(volume_list[int(self.Tree.current_volume)])
        except IndexError:
            return None
        
    def get_path_list(self,search_tag):
        """
        Get a list of all of the path items in the data structure indicated by 
        search_tag (FixedImage, Volume, Cal)
        """
        item_list = []
        # First find the specified pose node
        iter = self.Tree.iter(tag = search_tag)
        for item in iter:
            try:
                item_list.append(str(item.path))
            except AttributeError:
                # there is no path at this node, prune it from the tree
                parent = item.getparent()
                print "Removing element with no path"
                parent.remove(item)
        return item_list
    
    def get_unique_path_list(self,search_tag):
        """
        Like get_path_list, except only returns unique path names instead of
        one for every frame.
        
        Get a list of all of the path items in the data structure indicated by 
        search_tag (FixedImage, Volume, Cal)
        """
        item_list = []
        # First find the specified pose node
        iter = self.Tree.iter(tag = search_tag)
        for item in iter:
            if str(item.path) not in item_list:
                item_list.append(str(item.path))
        return item_list
    
    def get_current_cal(self):
        """
        returns the path to the current cal
        """
        cal_list = []
        # First find the specified pose node
        iter = self.Tree.iter(tag = 'Cal')
        for item in iter:
            cal_list.append(item.path)
        return str(cal_list[int(self.Tree.current_image)])
    
    def image_count(self):
        """
        Count the number of FixedImage tags
        """
        item_list = []
        # First find the specified pose node
        iter = self.Tree.iter(tag = 'FixedImage')
        for item in iter:
            item_list.append(item.path)
        return len(item_list)
    
    def frame_count(self):
        """
        Count the number of Frame tags
        """
        item_list = []
        # First find the specified pose node
        iter = self.Tree.iter(tag = 'Frame')
        for item in iter:
            item_list.append(item)
        return len(item_list)
    
    def add_volume(self,vol_path,Frame=None):
        """
        To add a volume to the data structure, supply the path
        to the image location.
        If a Frame is specified, the volume path will only be added to that 
        node, otherwise, this method will automatically iterate through 
        all of the frames and add the volume path to each one.
        """
        frame_iter = self.Tree.iter(tag='Frame')
        i = 0
        if Frame is None:
            for Frame in frame_iter:
                Volume = etree.SubElement(Frame, 'Volume')
                Volume.path = vol_path
                i += 1
        else:
            Volume = etree.SubElement(Frame, 'Volume')
            Volume.path = vol_path
            i += 1
            
        print "Volume added to %i image(s)" % i
        
    def remove_volume(self,vol_path):
        """
        Will remove the Volume node with the given volume path from all
        frames.  Be careful, this function will delete pose data that is 
        associated with removed volume. 
        """
        
        for vol in self.Tree.iter('Volume'):
            if vol.path == vol_path:
                Frame = vol.getparent()
                Frame.remove(vol)
            
    
    def add_cal(self,cal_path,img_path=None):
        """
        To add a calibration to the data structure, supply the path
        to the cal location. 
        
        If a Fixed img_path is specified, the volume path will only be added to that 
        node, otherwise, this method will automatically iterate through 
        all of the images and add the cal path to each one.
        
        cal_path is the path to a consolidated external calibration file.
    
        returns void
        """
        image_iter = self.Tree.iter(tag='FixedImage')
        i = 0
        for Image in image_iter:
            if (Image.path == img_path) or img_path is None:
                Cal = etree.SubElement(Image, 'Cal')
                Cal.path = cal_path
                i += 1
        print "Calibration added to %i image(s)" % i
    
    def add_color_window(self,image_path,value):
        """
        Add a color level to the fixed image.  
        """
        image_iter = self.Tree.iter(tag = 'FixedImage')
        for image in image_iter:
            if image.path == image_path:
                image.color_window = str(value)
                return
        print "add_color_window: No node exists for the specified paths"
    
    def get_color_window(self,image_path):
        """
        Get a color level to the fixed image.  
        """
        image_iter = self.Tree.iter(tag = 'FixedImage')
        for image in image_iter:
            if image.path == image_path:
                try:
                    value = int(image.color_window)
                    return value
                except:
                    return None
        print "get_color_window: No node exists for the specified paths"
        
    def add_current_color_window(self,value):
        """
        uses built-in index to automatically get the current image and volume
        to pass to the add_color_window function
        """
        self.add_color_window(self.get_current_image_path(),value)
    
    def get_current_color_window(self):
        """
        uses built-in index to automatically get the current image and volume
        to pass to the add_color_window function
        """
        return self.get_color_window(self.get_current_image_path())
                                  
    def add_window_level(self,image_path,value):
        """
        Add a window level to the fixed image.  
        """
        image_iter = self.Tree.iter(tag = 'FixedImage')
        for image in image_iter:
            if image.path == image_path:
                image.window_level = str(value)
                return
        print "add_window_level: No node exists for the specified paths"
    
    def get_window_level(self,image_path):
        """
        Get a window level to the fixed image.  
        """
        image_iter = self.Tree.iter(tag = 'FixedImage')
        for image in image_iter:
            if image.path == image_path:
                try:
                    value = int(image.window_level)
                    return value
                except:
                    return None
        print "get_window_level: No node exists for the specified paths"
        
    def add_current_window_level(self,value):
        """
        uses built-in index to automatically get the current image and volume
        to pass to the add_window_level function
        """
        self.add_window_level(self.get_current_image_path(),value)
    
    def get_current_window_level(self):
        """
        uses built-in index to automatically get the current image and volume
        to pass to the add_window_level function
        """
        return self.get_window_level(self.get_current_image_path())
    
    def add_roi(self,image_path,volume_path,roi):
        """
        Add a region of interest to the fixed image.  
        roi is specified as a list of 4 integers representing 2x2 Array 
        specifying the top left corner and the bottom right corner of the 
        region of the image that the metric  should be applied to.  
        [TopRightX,TopRightY,BottomX,BottomY]
        """
        #ROI.description = "ROI is a rectangle: \
        #    [TopRightX,TopRightY,BottomLeftX,BottomLeftY]"
        # First find the specified pose node
        image_iter = self.Tree.iter(tag = 'FixedImage')
        for image in image_iter:
            if image.path == image_path:
                Frame = image.getparent()
                vol_iter = Frame.iter(tag = 'Volume')
                for vol in vol_iter:
                    if vol.path == volume_path:
                        vol.roi = str(roi)
                        vol.roi_doc = "ROI is a rectangle: "\
                                "[TopRightX,TopRightY,BottomLeftX,BottomLeftY]"
                        return
        print "add_roi: No node exists for the specified paths"
    
    def get_roi(self,image_path,volume_path):
        """
        Given a fixed image element and an index, this function will 
        return the roi in python list format.  Returns ROI in numpy format.
        Returns None if no ROI is set at the requested node.
        """
        # First find the specified pose node
        image_iter = self.Tree.iter(tag = 'FixedImage')
        for image in image_iter:
            if image.path == image_path:
                Frame = image.getparent()
                vol_iter = Frame.iter(tag = 'Volume')
                for vol in vol_iter:
                    if vol.path == volume_path:
                        roi = None
                        # We use iter to check if there is actually a roi
                        # associated with this element, if none, return
                        for el in vol.iter('roi'):
                            roi=el
                        if roi==None: return None
                        roi_str = str(vol.roi)
                        # strip brackets from string and split
                        # Returns None if no pose exists at this node
                        # use reg expression to split commas and space\
                        # fist strip leading and trailing spaces, and []'s
                        #sep = re.compile(r',*\s*')
                        #print "DATA roi_string: %s" % roi_str
                        # Strip out all brackets to make the roi a list of 4 integers
                        if roi_str.count(','):
                            # roi is comma delimited
                            roi = np.asarray(roi_str.replace('[','').
                                                 replace(']','').
                                                 split(','),'i')
                        elif roi_str.count(' '):
                            # roi is space delimited
                            roi = np.asarray(roi_str.replace('[','').
                                                 replace(']','').
                                                 split(),'i')
                        roi = roi.astype('i')
                        return roi
        print "get_roi: No node exists for the specified paths"
    
    def get_current_roi(self):
        """
        uses built-in index to automatically get the current image and volume
        to pass to the get_roi function
        """
        return self.get_roi(self.get_current_image_path(),
                             self.get_current_volume_path())
        
    def add_current_roi(self,roi):
        """
        uses built-in index to automatically get the current image and volume
        to pass to the add_roi function
        """
        image_path = self.get_current_image_path()
        volume_path = self.get_current_volume_path()
        self.add_roi(image_path, volume_path, roi)
    
    def add_roi_all_frames(self,volume_path,roi):
        """
        ***THIS FUNCTION IS NOT MULTIPLANE COMPATIBLE***  ROI should 
        be distinguished based on which fixed image and which volume it is
        associated with. 
        
        Given a volume and an roi, this function will 
        iterate through all frames and apply the roi to the specified volume
        in each frame.  
        """
        for img in self.Tree.iter(tag = 'FixedImage'):
            print img.path
            print volume_path
            self.add_roi(str(img.path), volume_path, roi)
                    
    
    def add_center(self,image_path,volume_path,center):
        """
        Add a volume center to the volume node.  
        """
        image_iter = self.Tree.iter(tag = 'FixedImage')
        for image in image_iter:
            if image.path == image_path:
                Frame = image.getparent()
                vol_iter = Frame.iter(tag = 'Volume')
                for vol in vol_iter:
                    if vol.path == volume_path:
                        vol.center = str(center)
                        return
        print "add_center: No node exists for the specified paths"
    
    def get_center(self,image_path,volume_path):
        """
        Given a fixed image element and an index, this function will 
        return the volume center.  Returns center in numpy format.
        """
        # First find the specified pose node
        image_iter = self.Tree.iter(tag = 'FixedImage')
        for image in image_iter:
            if image.path == image_path:
                Frame = image.getparent()
                vol_iter = Frame.iter(tag = 'Volume')
                for vol in vol_iter:
                    if vol.path == volume_path:
                        center_str = str(vol.center)

                        #print "DATA pose_string: %s" % pose_string
                        # Strip out all brackets
                        center = np.asarray(center_str.replace('[','').
                                                 replace(']','').
                                                 replace('(','').
                                                 replace(')','').
                                                 split(','),'f')
                        return center
        print "get_center: No node exists for the specified paths"
    
    def get_current_center(self):
        """
        uses built-in index to automatically get the current image and volume
        to pass to the get_center function
        """
        return self.get_center(self.get_current_image_path(),
                             self.get_current_volume_path())
        
    def add_current_center(self,center):
        """
        uses built-in index to automatically get the current image and volume
        to pass to the add_center function
        """
        image_path = self.get_current_image_path()
        volume_path = self.get_current_volume_path()
        self.add_center(image_path, volume_path, center)
    
    def add_center_all_volumes(self,vol_path,center):
        """
        Add a center to all volumes with the given path.
        """
        for vol in self.Tree.iter('Volume'):
            if vol.path == vol_path:
                vol.center = str(center)
    
    def get_path(self,Element):
        """
        Given a Volume, FixedImage, or Cal element, this 
        function will return a python string file path.
        """
        try:
            assert(Element.path.tag == 'path')
        except:
            print 'Error. An element with a path tag is required.'
            raise 
            
        path = Element.path.pyval
        return path
    
    def add_pose(self, image_path, volume_path, pose, type='current'):
        """
        Add a pose to the volume at a specified frame.  
               
        Type is either: current, initial or optimized.  To indicate the type of 
        pose that is being added to the data structure.
            current_pose is updated with every keyboard press.
            initial_pose is the current pose when the optimizer is started.
            optimized_pose is the result of the optimization. 
        
        Data is stored as a string so that it can be written to xml.  To 
        retrieve them in numpy format, use get_pose
        """
        known_pose_types = ['current', 'initial', 'optimized']
        if type not in known_pose_types:
            type = 'current'
        # First find the specified pose node
        image_iter = self.Tree.iter(tag = 'FixedImage')
        for image in image_iter:
            if image.path == image_path:
                Frame = image.getparent()
                vol_iter = Frame.iter(tag = 'Volume')
                for vol in vol_iter:
                    if vol.path == volume_path:
                        if type == 'current':
                            vol.__setattr__('current_pose',str(pose))
                            # If optimized and current poses are being set at 
                            # the same time, the current pose has to the set 
                            # first so that the isoptimized flag will not 
                            # be overwritten
                            vol.isoptimized = str(False)
                        elif type == 'initial':
                            vol.__setattr__('initial_pose',str(pose))
                        elif type == 'optimized':
                            vol.__setattr__('optimized_pose',str(pose))
                            # Set a flag to indicate that this volume at this frame is optimized
                            vol.isoptimized = str(True)
                        return 
        #print "No node exists for the specified paths"
            
        
    def get_pose(self, image_path, volume_path, type='current'):
        """
        Get a pose from the deisred node.  A node is specified by giving 
        a fixed image path and volume path.  Each frame is searched until 
        one with specified fixed image is found, then that frame is searched 
        for a node with the specified volume path.  The pose is stored in
        the volume node (Frame/Volume/pose).  
        
        Type is either: current, initial or optimized.  To indicate the type of 
        pose that is being added to the data structure.
            current_pose is updated with every keyboard press.
            initial_pose is the current pose when the optimizer is started.
            optimized_pose is the result of the optimization. 
        
        Pose is returned as a numpy array. 
        """
        
        known_pose_types = ['current', 'initial', 'optimized']
        if type not in known_pose_types:
            type = 'current'
        
        #print "Searching for image: %s" % image_path
        #print "Searching for vol: %s" % volume_path
        # First find the specified pose node
        image_iter = self.Tree.iter(tag = 'FixedImage')
        for image in image_iter:
            #print image.path
            if image.path == image_path:
                Frame = image.getparent()
                vol_iter = Frame.iter(tag = 'Volume')
                for vol in vol_iter:
                    #print vol.path
                    if vol.path == volume_path:
                        try:
                            if type == 'current':
                                pose_string = str(vol.__getattr__('current_pose'))
                            elif type == 'initial':
                                pose_string = str(vol.__getattr__('initial_pose'))
                            elif type == 'optimized':
                                pose_string = str(vol.__getattr__('optimized_pose'))
                        except AttributeError:
                            #no pose with given tag, return None
                            return None
                        # strip brackets from string and split
                        # Returns None if no pose exists at this node
                        # use reg expression to split commas and space\
                        # fist strip leading and trailing spaces, and []'s
                        sep = re.compile(r',*\s*')
                        #print "DATA pose_string: %s" % pose_string
                        pose = np.asarray(sep.split(pose_string.strip('[] ')),'f')
                        return pose
    
    def get_all_poses(self,vol_path=None):
        """
        Get all poses associated with the given vol_path, if no path is 
        given, all of the poses are returned as a numpy array in the format:
            [Frame,Volume,Dimension]
        eg for 46 frames/images, with 4 volumes will have a shape of [46,4,6]
        if only one volume is requested, the shape would be [46,1,6]
        
        """
        
        default_pose = np.zeros(6)
        sep = re.compile(r',*\s*')
        poses = []
        if vol_path is None:
            pass
            #vol_path = self.get_current_volume_path()
        for Image in self.Tree.iter('FixedImage'):
            poses.append([])
            for Volume in Image.getparent().iter('Volume'):
                if (vol_path == Volume.path) or (vol_path == None):
                    #print Volume.path
                    try:
                        pose_string = Volume.current_pose
                        pose = np.asarray(sep.split(str(pose_string).strip('[] ')),'f')
                    except:
                        pose = default_pose
                    
                    # either append current pose or default if there is
                    # no pose set yet
                    poses[-1].append(pose)
        return np.asarray(poses)
            
    
    def get_current_pose(self):
        """
        uses built-in index to automatically get the current image and volume
        to pass to the get_pose function
        """
        return self.get_pose(self.get_current_image_path(),
                             self.get_current_volume_path())
        
    def add_current_pose(self,pose,type=None):
        """
        uses built-in index to automatically get the current image and volume
        to pass to the add_pose function
        """
        image_path = self.get_current_image_path()
        volume_path = self.get_current_volume_path()
        self.add_pose(image_path, volume_path, pose, type)
    
    def delete_pose(self,image_path,vol_path):
        """
        Delete the current pose from the specified node. Returns 1 if the 
        desired pose is found and deleted, 0 otherwise.
        """
        image_iter = self.Tree.iter(tag = 'FixedImage')
        for image in image_iter:
            if image.path == image_path:
                Frame = image.getparent()
                vol_iter = Frame.iter(tag = 'Volume')
                for vol in vol_iter:
                    if vol.path == volume_path:
                        try:
                            # See if there is indeed a pose at this node
                            pose = vol.current_pose
                            vol.remove(pose)
                            return 1
                        except:
                            # node found, but no pose here
                            return 0
        # desired node not found
        return 0
    
    def delete_current_pose(self):
        """
        Delete the current pose from the specified node. Returns 1 if the 
        desired pose is found and deleted, 0 otherwise.
        """ 
        self.delete_pose(self.get_current_image_path(),
                         self.get_current_volume_path())                    
    
    
    def export_poses(self,filename):
        """
        All poses contained in the data structure will be saved to the
        filename provided.   
        """
        # Get all poses for all volumes
        poses = self.get_all_poses()
        # Find the number of frames
        n_frames = self.frame_count()
        # Get a list of volumes associated with the poses
        vol_list = self.get_unique_path_list('Volume')
        # Create a header string
        out_string = 'Euler Sequence: 213 [VTK]\n\t'
        for vol in vol_list:
            out_string += ('%s\t\t\t\t\t\t' % str(vol))
        out_string += ('\nFrame')
        for vol in vol_list:
            out_string += ('\tX-Trans\tY-Trans\tZ-Trans\tX-Rot(deg)\tY-Rot(deg)\tZ-Rot(deg)')
        for frame in range(n_frames):
            image_path = self.get_path(self.Tree.Frame[frame].FixedImage)
            out_string += '\n%i'%frame
            #print image_path
            for volume_path in vol_list:
                #print volume_path
                pose = self.get_pose(image_path, volume_path, 'current')
                if pose == None:
                    pose = np.zeros(6)
                out_string += ('\t%f\t%f\t%f\t%f\t%f\t%f' % 
                                          (pose[0],pose[1],pose[2],
                                           pose[3]*180/np.pi,pose[4]*180/np.pi,pose[5]*180/np.pi))
        fid = open(filename,'w')
        fid.write(out_string)
        fid.close()
    
    def import_poses(self,filename):
        """
        All poses contained in the file will be applied to the
        current data structure provided.   
        
        NOT TESTED
        """
        # Parse file
        fid = open(filename,'r')
        seq = fid.readline()
        file_vol_list = fid.readline().split()
        headers = fid.readline()
        poses_str = fid.read()
        poses = np.asarray(poses_str.split(),'f')
        elements = len(poses)
        n_vols_file = len(file_vol_list)
        n_frames_file = elements/(6*n_vols_file+1)
        poses = poses.reshape(n_frames_file,6*n_vols_file+1)
        fid.close()
        
        # convert from deg to rad
        for v in range(n_vols_file):
            poses[:,(v*6+4):(v*6+7)] = poses[:,(v*6+4):(v*6+7)]*np.pi/180
                
        # Find the number of frames
        n_frames = self.frame_count()
        # Get a list of volumes associated with the poses
        vol_list = self.get_unique_path_list('Volume')

        for frame in range(n_frames):
            image_path = self.get_path(self.Tree.Frame[frame].FixedImage)
            #print image_path
            for v,volume_path in enumerate(vol_list):
                #print volume_path
                self.add_pose(image_path, volume_path, pose[frame,(v*6+1):(v*6+7)], 'current')
    
    def create_backup(self):
        """
        Creates a copy of the all data in case the user wants to restore it 
        later.  May be useful for implementing a simple undo feature.
        """
        # just to be safe, save old data as a backup
        self.BackupData = self.tostring()
        # Get a baseline for the number of data items when the file is backedup
        self.backup_poses_count = self.countdescendants()
    
    def restore_backup(self):
        """
        Restore a backup copy of the data.  Be careful to make sure that the 
        backup data is up to date!  
        May be useful for implementing a simple undo feature.
        """
        # read in backup data
        nAdditions = self.countdescendants() - self.backup_poses_count
        print "There have been %s additions to the data structure since the "\
              "backup was created" % str(nAdditions)
        self.Tree = objectify.fromstring(self.BackupData)
    
        
    def isoptimized(self, image_path, volume_path):
        """
        This function will check for the optimized flag for the image/volume pair
        """
        # First find the specified pose node
        for image in self.Tree.iter(tag = 'FixedImage'):
            #print image.path
            if image.path == image_path:
                for vol in image.getparent().iter('Volume'):
                    if vol.path == volume_path:
                        try:
                            test = vol.isoptimized.text
                            #print "Data is optimized: %s" % str(test)
                            if str(test)==str(True):
                                return True
                            else:
                                return False
                        except AttributeError:
                            # No isoptimized attribute exists
                            return False
        #print "No node exists for the specified paths"
    
    def add_display_settings(self, threshold, level):
        """
        Add the display settings for how the volume is rendered in the DRR.
        settings are added to the current node (image/volume pair)
        """
        image_path = self.get_current_image_path()
        volume_path = self.get_current_volume_path()
        # First find the specified pose node
        image_iter = self.Tree.iter(tag = 'FixedImage')
        for image in image_iter:
            #print image.path
            if image.path == image_path:
                Frame = image.getparent()
                vol_iter = Frame.iter(tag = 'Volume')
                for vol in vol_iter:
                    #print vol.path
                    if vol.path == volume_path:
                        vol.theshold = threshold
                        vol.level = level
                        return
        print "No node exists for the specified paths"
    
    def get_display_settings(self,image_path=None,volume_path=None):
        """
        Get the display settings for how the volume is rendered in the DRR.
        """
        if image_path == None:
            image_path = self.get_current_image_path()
        if volume_path == None:
            volume_path = self.get_current_volume_path()
        # First find the specified pose node
        image_iter = self.Tree.iter(tag = 'FixedImage')
        threshold = None
        level = None 
        for image in image_iter:
            #print image.path
            if image.path == image_path:
                Frame = image.getparent()
                vol_iter = Frame.iter(tag = 'Volume')
                for vol in vol_iter:
                    #print vol.path
                    if vol.path == volume_path:
                        try:
                            threshold = float(vol.theshold) 
                            level = float(vol.level)
                            return (threshold, level)
                        except AttributeError:
                            # Keep going if there is nothing at this node
                            pass
                            
        print "No node exists for the specified paths"
        return (threshold, level)
    
    def set_global_display_settings(self,threshold,level):
        """
        Global display settings that could be used as defaults for all models
        in all frames.  This is usefuls so that individual settings do not
        have to be made for every frame. Threshold and level are returned 
        in a tuple.
        """
        try:
            self.Tree.DisplaySettings.threshold.clear()
            self.Tree.DisplaySettings.level.clear()
        except:
            # no settings in file
            pass
        self.Tree.DisplaySettings.threshold = str(threshold)
        self.Tree.DisplaySettings.level = str(level)
    
    def get_global_display_settings(self):
        """
        Global display settings that could be used as defaults for all models
        in all frames.  This is usefuls so that individual settings do not
        have to be made for every frame. Threshold and level are returned 
        in a tuple.
        """
        threshold = float(self.Tree.DisplaySettings.threshold)
        level = float(self.Tree.DisplaySettings.level)
        return (threshold,level)
    
    def import_poses_from_project(self,xml_filename,volume_path,pose_type='current'):
        """
        Given an xml project filename, import the poses into the current 
        project.  The projects must have the same number of frames.
            
        Different pose types can be specified:
            current_pose is updated with every keyboard press.
            initial_pose is the current pose when the optimizer is started.
            optimized_pose is the result of the optimization.
        """
        pose_data = DataStructure()
        new_pose_list = []
        old_frame_list = []
        pose_data.load(xml_filename)
        
        if pose_type == 'current':
            pose_iter = pose_data.Tree.iter('current_pose')
        elif pose_type == 'optimized':
            pose_iter = pose_data.Tree.iter('optimized_pose')
        elif pose_type == 'initial':
            pose_iter = pose_data.Tree.iter('initial_pose')
        else:
            print "Type %s, not understood" % str(pose_type)
            return
        
        for pose in pose_iter:
            new_pose_list.append(pose) 
        
        frame_iter = self.Tree.iter('Frame')
        for frame in frame_iter:
            old_frame_list.append(frame)
        
        # if there ar more poses than frames, get out of here!
        if len(new_pose_list) != len(old_frame_list):
            print "Error. The number of poses does not equal the number of Frames!"
            return
        
        for i,frame in enumerate(frame_iter):
            #print image.path
            self.add_pose(frame.FixedImage.path, volume_path, new_pose_list[i], pose_type)
        
        print "Added %i poses to the current project" % i
    
    def remove_all_images(self):
        """
        Remove all fixed images from data structure!
        """
        image_iter = self.Tree.iter(tag = 'FixedImage')
        for Image in image_iter:
            Frame = Image.getparent()
            Frame.remove(Image)
    
    def remove_all_volumes(self):
        """
        Remove all volumes from data structure!
        """
        vol_iter = self.Tree.iter(tag = 'Volume')
        for Volume in vol_iter:
            Frame = Volume.getparent()
            Frame.remove(Volume) 
    
    def remove_all_cals(self):
        """
        Remove all calibrations from data structure!
        """
        cal_iter = self.Tree.iter(tag = 'Cal')
        for Cal in cal_iter:
            Frame = Cal.getparent()
            Frame.remove(Cal)    
    
    def countdescendants(self,node=None):
        """
        Count the total number of descendents from the given node (root if 
        none given).  This function might be useful to test whether any 
        modifications have been made to the data.
        """
        des = []
        if node is None:
            iter = self.Tree.iterdescendants()
        else:
            iter = nodee.iterdescendants()
        for i in iter:
            des.append(i)
        return len(des)
    
    def isdirty(self):
        """
        will return True if any descendents have been added since the data 
        structure was created/opened
        """
        if self.countdescendants() == self._clean_descendents:
            return False
        else:
            return True
    
    def add_raw_poses(self,poses):
        """
        If you just want to use the project file to store poses, and 
        no file paths, etc.  poses is a numpy array is in the format: 
        (frame, volume, dimension). This method will create the necessary
        frames and volumes to hold the data. 
        """
        shape = poses.shape
        fake_images = []
        # make a list of stirng integers to act like image file paths
        for i in range(shape[0]): fake_images.append(str(i))
        self.add_image_sequence(fake_images)
        
        if len(shape) == 2:
            # only one volume
            self.add_volume('0')
        if len(shape) == 3:
            # most common usage is for multiple volumes 
            for i in range(shape[1]):
                # iterate over each volume
                self.add_volume(str(i))
                for j in range(shape[0]):
                    #iterate over each frame
                    self.add_pose(str(j), str(i), poses[j,i,:])
    
    def tolist(self,element_list):
        """
        convert a list of objectify elements into a python list
        """
        py_list = []
        for el in element_list:
            py_list.append(el.pyval)
        
        return py_list
    
    def todict(self,element_node):
        """
        will extract all attributes from the given node (Not Recursive)
        and place them into a python dictionary
        """
        pydict = {}
        for value in element_node.iterchildren():
            if value.tag in pydict:
                # multiple attribute of the same name are represented by a list
                children = pydict[value.tag]
                if not isinstance(children, list):
                    children = [children]
                    pydict[value.tag] = children
                children.append(value.pyval)
            else:
                pydict[value.tag] = value.pyval
        return pydict
    
    def fromdict(self,parent_node,child_dict):
        """
        will create child attributes in the given parent_node corresponding 
        to each key in the dictionary.  Previous settings will all be erased!
        """
        if not isinstance(child_dict,dict):
            print 'Error.  Must be a dictionary type'
            return
        parent_node.clear()
        for key,value in child_dict.iteritems():
            parent_node.addattr(key,value)
            
    def load(self,path):
        """
        load data structure from an xml file.  WARNING: all current data will
        be discarded and replaced by the new self.
        """
        try:
            fid = open(path)
        except IOError:
            print 'path is not valid, nothing done'
            raise
        
        # just to be safe, save old data as a backup
        self.BackupData = self.tostring()
        # read in new data file
        xml_string = fid.read()
        # read in new data
        self.Tree = objectify.fromstring(xml_string)
        # Get a baseline for the number of data items when the file is clean
        self._clean_descendents = self.countdescendants()
        # If there is a root_dir, change the cwd to it so that relative paths 
        # will work, will this break anything else???
        # Alternatively, we could insert a new root_dir at the beginning of 
        # all of the paths in the project file.
        try:
            root_dir = str(self.Tree.root_dir)
            print "Changing cwd to root directory: %s" % str(root_dir)
            try:
                os.chdir(root_dir)
            except:
                print "Error changing to root directory."
                print "    Either manually os.chdir to correct root or modify"
                print "    the xml file and reload."
                print "    Bad dir: %s" % str(root_dir)
        except AttributeError:
            # No root dir, path must be absolute
            print "No root directory found. Assuming all paths are absolute"
    
    def load_string(self,xml_string):
        """
        load data structure from an xml string.  WARNING: all current data will
        be discarded and replaced by the new self.
        """       
        # just to be safe, save old data as a backup
        self.BackupData = self.tostring()

        # read in new data
        self.Tree = objectify.fromstring(xml_string)
        # Get a baseline for the number of data items when the file is clean
        self._clean_descendents = self.countdescendants()
        # If there is a root_dir, change the cwd to it so that relative paths 
        # will work, will this break anything else???
        # Alternatively, we could insert a new root_dir at the beginning of 
        # all of the paths in the project file.
        try:
            root_dir = str(self.Tree.root_dir)
            print "Changing cwd to root directory: %s" % str(root_dir)
            try:
                os.chdir(root_dir)
            except:
                print "Error changing to root directory."
                print "    Either manually os.chdir to correct root or modify"
                print "    the xml file and reload."
                print "    Bad dir: %s" % str(root_dir)
        except AttributeError:
            # No root dir, path must be absolute
            print "No root directory found. Assuming all paths are absolute"
     
    def save(self, path, make_relative=False):
        """
        save data structure to an xml file.  make_relative will find the
        common prefix for all filepaths and save that as the data root 
        directory.  If the file is opened on another computer the user would 
        then just have to store the data in the same directory tree structure 
        and can make the root directory the current working directory.
        """
        try:
            fid = open(str(path),'w')
        except IOError:
            print 'path is not valid, nothing done'
            raise
        
        if make_relative:
            # iterate through all of the paths
            path_list = []
            for path in self.Tree.iter('path'):
                path_list.append(str(path))
            self.Tree.root_dir = os.path.commonprefix(path_list)
            for i,path in enumerate(self.Tree.iter('path')):
                parent = path.getparent()
                # Replace the path with a path relative to the root_dir
                parent.path = path_list[i].replace(str(self.Tree.root_dir),
                                                   '.'+os.sep)
                
            
        
        xml_string = self.tostring()
        fid.write(xml_string)
        fid.close()
        # Reset number of clean descendents 
        self._clean_descendents = self.countdescendants()
    
    def tostring(self,node=None):
        """
        Return a pretty printed string representation of the current data
        structure.
        """
        if node is None:
            return etree.tostring(self.Tree, pretty_print=True)
        else:
            return etree.tostring(node, pretty_print=True)
    
    def dump(self,node=None):
        """
        Return a pretty printed string representation of the current data
        structure.
        """
        if node is None:
            print objectify.dump(self.Tree)
        else:
            print objectify.dump(node)



#===============================================================================
# The next classes are for practice only right now
# I am testing out how to store data in a model that is compatible 
# with QT's model/view classes.
#===============================================================================
class TreeItem:
    def __init__(self, data, parent=None):
        self.parentItem = parent
        self.itemData = data
        self.childItems = []

    def appendChild(self, item):
        self.childItems.append(item)

    def child(self, row):
        return self.childItems[row]

    def childCount(self):
        return len(self.childItems)

    def columnCount(self):
        return len(self.itemData)

    def data(self, column):
        return self.itemData[column]

    def parent(self):
        return self.parentItem

    def row(self):
        if self.parentItem:
            return self.parentItem.childItems.index(self)

        return 0

class QTDataModel(DataStructure,QtCore.QAbstractItemModel):
    """
    
    """
    def __init__(self, parent=None):
        QtCore.QAbstractItemModel.__init__(self, parent)
        rootData = []
        rootData.append(QtCore.QVariant("Title"))
        rootData.append(QtCore.QVariant("Summary"))
        self.rootItem = TreeItem(rootData)
        self.rootItem.appendChild(TreeItem(['test'],self.rootItem))
        self.rootItem.appendChild(TreeItem(['foo'],self.rootItem))
        self.rootItem.appendChild(TreeItem(['bar'],self.rootItem))
        #self.setupModelData(data.split("\n"), self.rootItem)
    
    def index(self, row, column, parent):
        return QtCore.QModelIndex()
    def parent(self, index):
        return QtCore.QModelIndex()
    def rowCount(self, parent):
        return 4
    def columnCount(self, parent):
        return 2
    def data(self, index, role):
        if not index.isValid():
            return QtCore.QVariant()

        if role != QtCore.Qt.DisplayRole:
            return QtCore.QVariant()

        item = index.internalPointer()

        return QtCore.QVariant(item.data(index.column()))

def test_qtModel():
    
    app = QtGui.QApplication(sys.argv)

    model = QTDataModel()

    view = QtGui.QTreeView()
    view.setModel(model)
    view.setWindowTitle("Simple Tree Model")
    view.show()
    sys.exit(app.exec_())

def test_qtTreeWidget():
    
    app = QtGui.QApplication(sys.argv)

    tree = QtGui.QTreeWidget()
    tree.setColumnCount(1)
    items = []
    details = []
    frames = []
    cals = []
    images = []
    vols = []
    verts = []
    
    
    for i in range(10):
        frames.append(QtGui.QTreeWidgetItem(tree))
        frames[i].setText(0,"Frame %i" % i)
        cals.append(QtGui.QTreeWidgetItem(frames[-1]))
        cals[-1].setText(0,"Cal %i" % i)
        vols.append(QtGui.QTreeWidgetItem(frames[-1]))
        vols[-1].setText(0,"Volume %i" % i)
        for j in range(4):
            verts.append(QtGui.QTreeWidgetItem(vols[-1]))
            verts[-1].setText(0,"Body %i" % j)
        images.append(QtGui.QTreeWidgetItem(frames[-1]))
        images[-1].setText(0,"Fixed Image %i" % i)
    
    def itemPressed(item,index):
        print item
        print "You selected %s (row %i)" % (item.text(index), index)
           
    tree.insertTopLevelItems(0,frames)
    tree.setHeaderLabels(QtCore.QStringList(["Data Items"]))
    tree.setWindowTitle("JointTrack Data Viewer")
    tree.connect(tree,QtCore.SIGNAL("itemPressed(QTreeWidgetItem *,int)"),itemPressed)
    tree.show()
    sys.exit(app.exec_())
    
    
    
#===============================================================================
# This function will load the Gold Standard 2D3D data set 
#===============================================================================
def gold_standard():
    Data = DataStructure()
    fixed_image_vert1_filename = []
    fixed_image_vert1_filename.append(
            'C:/Users/bryan/bryan-code/2D3D/vert1/fluoro/ushortim042-AP-flip.tif')
    fixed_image_vert1_filename.append(
            'C:/Users/bryan/bryan-code/2D3D/vert1/fluoro/ushortim080-LAT-flip.tif')
    fixed_image_vert2_filename = []
    fixed_image_vert2_filename.append(
            'C:/Users/bryan/bryan-code/2D3D/vert2/fluoro/ushortim075-AP-flip.tif')
    fixed_image_vert2_filename.append(
            'C:/Users/bryan/bryan-code/2D3D/vert2/fluoro/ushortim040-LAT-flip.tif')
    
    # fixed_image_filename structure: fixed_image_filename[vert][projection]
    fixed_image_filename = [fixed_image_vert1_filename,
                            fixed_image_vert2_filename]
    
    cal_file_vert1 = []
    cal_file_vert1.append(
            'C:/Users/bryan/bryan-code/2D3D/vert1/fluoro/ushortim042-AP.cal')
    cal_file_vert1.append(
            'C:/Users/bryan/bryan-code/2D3D/vert1/fluoro/ushortim080-LAT.cal')
    
    cal_file_vert2 = []
    cal_file_vert2.append(
            'C:/Users/bryan/bryan-code/2D3D/vert2/fluoro/ushortim075-AP.cal')
    cal_file_vert2.append(
            'C:/Users/bryan/bryan-code/2D3D/vert2/fluoro/ushortim040-LAT.cal')
    
    # cal_file structure: cal_file[vert][proj]
    cal_file = [cal_file_vert1,cal_file_vert2]
    
    volume_filename = []
    volume_filename.append(
            'C:/Users/bryan/bryan-code/2D3D/vert1/CT/ucharCTv1r1.mhd')
    volume_filename.append(
            'C:/Users/bryan/bryan-code/2D3D/vert2/CT/ucharCTv2r4.mhd')
    
    sta_file = []
    sta_file.append('C:/Users/bryan/bryan-code/2D3D/vert1/CT/ucharCTv1r1.sta')
    sta_file.append('C:/Users/bryan/bryan-code/2D3D/vert2/CT/ucharCTv2r4.sta')
    
    roi_vert1_ap = []
    roi_vert1_ap.append([[268,313],[344,166]])
    roi_vert1_ap.append([[209,341],[283,175]])
    roi_vert1_ap.append([[155,331],[223,183]])
    
    roi_vert1_lat = []
    roi_vert1_lat.append([[273,371],[343,204]])
    #roi_vert1_lat.append([[210,370],[277,187]])
    roi_vert1_lat.append([[187,301],[370,234]])
    roi_vert1_lat.append([[160,347],[223,190]])
    
    roi_sim_lat = []
    roi_sim_lat.append([[273,204],[343,371]])
    roi_sim_lat.append([[210,187],[277,370]])
    roi_sim_lat.append([[160,190],[223,347]])
    
    roi_vert2_ap = []
    roi_vert2_ap.append([[332,369],[403,205]]) #vert 2, body 1
    roi_vert2_ap.append([[275,387],[345,210]]) #vert 2, body 2
    roi_vert2_ap.append([[213,371],[291,227]]) #vert 2, body 3
    roi_vert2_ap.append([[154,380],[221,219]]) #vert 2, body 4
    roi_vert2_ap.append([[86, 366],[156,220]]) #vert 2, body 5
    
    roi_vert2_lat = []
    roi_vert2_lat.append([[337,299],[394,166]]) #vert 2, body 1
    roi_vert2_lat.append([[273,282],[341,156]]) #vert 2, body 2
    roi_vert2_lat.append([[217,286],[284,146]]) #vert 2, body 3
    roi_vert2_lat.append([[152,285],[229,129]]) #vert 2, body 4
    roi_vert2_lat.append([[89 ,282],[170,107]]) #vert 2, body 5
    
    # roi structure: roi[vert][projection][body]
    roi_vert1 = [roi_vert1_ap,roi_vert1_lat,roi_sim_lat]
    roi_vert2 = [roi_vert2_ap,roi_vert2_lat]
    roi = [roi_vert1,roi_vert2]
    
    centers_vert1 = []
    centers_vert1.append([3.906, -24.738, -12.586])
    centers_vert1.append([3.906, 2.17, -7.378])
    centers_vert1.append([7.378, 27.342, -6.51])
    
    centers_vert2 = []
    centers_vert2.append([8.07674, -41.94694, -14.85078])
    centers_vert2.append([3.9081, -21.10374, -17.45618])
    centers_vert2.append([-0.26054, 0.78162, -21.10374])
    centers_vert2.append([-5.47134, 22.66698, -25.79346])
    centers_vert2.append([-10.16106, 45.07342, -27.87778])
    
    # centers structure: centers[vert][body]
    centers = [centers_vert1,centers_vert2]
    
    projection_range = range(len(fixed_image_filename))
    vert_range = range(len(volume_filename))
    center_range = []
    for vert in vert_range:
        center_range.append(range(len(centers[vert])))    
    
    
    # Frame 1 is vert 1, Frame 2 is vert 2  
    # Load Data elements into Data structure
    for vert in vert_range:
        Frame = Data.add_frame()
        vol = volume_filename[vert]
        Data.add_volume(vol,Frame)
                
        #for proj in projection_range:
        # Only look at one projection at a time [AP=0, LAT=1]
        proj = 1
        proj_dict = {}
        proj_dict[0] = 'AP'
        proj_dict[1] = 'LAT'
        img = fixed_image_filename[vert][proj]                                   
        Data.add_fixed_image(img,Frame)
        Data.add_cal(cal_file[vert][proj], img)
        for body in center_range[vert]:
            print "vert: %i, proj: %i, body: %i" % (vert, proj, body)
            print "    roi: %s" % str(roi[vert][proj][body])
            print "    img: %s" % img
            print "    vol: %s" % vol
            print "    proj: %s" % proj_dict[proj]
            #print "centers: ", centers[vert][body]
            #Data.add_roi(img,vol,roi[vert][proj][body])
            #Data.add_center(img,vol,centers[vert][body])
    
    Data.roi = roi
    Data.centers = centers
    
    return Data


#===============================================================================
# This function will load a data set of simulated (DRR instead of fluoro images) data
#===============================================================================
def simulated_data():
    Data = JT.Data.Data()
    fixed_image_filename = \
            r'C:\Users\bryan\bryan-code\trunk\Tests\SimulatedFixedImage.mhd'
    
    cal_file = \
            'C:/Users/bryan/bryan-code/2D3D/vert1/fluoro/ushortim080-LAT.cal'
    
    volume_filename = \
            'C:/Users/bryan/bryan-code/2D3D/vert1/CT/ucharCTv1r1.mhd'
    
    sta_file = 'C:/Users/bryan/bryan-code/2D3D/vert1/CT/ucharCTv1r1.sta'
    
    roi = []
    roi.append([[273,204],[343,371]])
    roi.append([[210,187],[277,370]])
    roi.append([[160,190],[223,347]])
    
    centers = []
    centers.append([3.906, -24.738, -12.586])
    centers.append([3.906, 2.17, -7.378])
    centers.append([7.378, 27.342, -6.51])
    
    body_range = range(len(centers))
 
    # Load Data elements into Data structure
    self.Frames.add()
    self.Frames[0].Volumes[0].self.\
        SetVolumeFilename(volume_filename)
        
    self.Frames[0].FixedImages.add()
    for body in body_range:
        #print "vert: %i, proj: %i, body: %i" % (vert, proj, body)
        #print "centers: ", centers[vert][body]
        self.Frames[0].Volumes[0].BodyCenters.append(centers[body])
        self.Frames[0].FixedImages[0].self.SetFileName(fixed_image_filename)
        self.Frames[0].FixedImages[0].calibration.LoadCalFile(cal_file)
        self.Frames[0].FixedImages[0].calibration.LoadStaFile(sta_file)
        self.Frames[0].Volumes[0].self.SetOriginalTransform(
                self.Frames[0].FixedImages[0].calibration.volumeTransform)
        self.Frames[0].FixedImages[0].ROIs.add()
        self.Frames[0].FixedImages[0].ROIs[body].data = roi[body]
    
    return Data
    
if __name__ == '__main__':
    #data = gold_standard()
    #print data.tostring()   
    #qtmodel = QTDataModel()
    test_qtTreeWidget()
        
